//
//  CartesianTopology.h
//  PIDAMRX
//
//  Created by Patrick Zulian on 4/7/11.
//  Copyright 2011 ICS - USI - Lugano. All rights reserved.
//

#ifndef PIDAMRX_CartesianTopology_H
#define PIDAMRX_CartesianTopology_H
#include <mpi.h>

#include "Array.h"
#include "Communicator.h"

namespace nsesolver {
    
    namespace mpi {
        class Neighbor {
        public:
            int dim, disp;
            int rank;
            
            
            Neighbor()
            : dim(0), disp(0), rank(0)
            {}
            
            Neighbor(const int dim, const int disp, const int rank)
            : dim(dim), disp(disp), rank(rank)
            {
                
            }
            
            inline
            int getRank() const
            {
                return rank;
            }
            
            inline
            int getDisplacement() const
            {
                return disp;
            }
            
            inline
            int getDimension() const
            {
                return dim;
            }
            
            friend
            std::ostream & operator <<(std::ostream & os, const Neighbor & neighbor)
            {
                os << "Dim: " << neighbor.getDimension() << "\t";
                os <<  "Disp: " << neighbor.getDisplacement() << "\t";
                os <<  "Rank: " << neighbor.getRank() << "\t";
                return os;
            }
        };
        
        
        /**
         * \brief The CartesianTopology class ... TODO Doc
         */ 
        class CartesianTopology {
        public:
            typedef  std::vector< Neighbor >::iterator NeighborIterator;
            
        private:
            Communicator communicator;
            MPI_Comm cartComm;
            std::vector< Neighbor > neighbors;
            Array< int > dimensions;
            Array< int > coordinates;
           
            void init();
        public:
            
            /**
             * \brief uses the MPI_Dims_create to genarate the processor grid
             * \param dim the number of dimension of the processor grid
             * \param communicator the communicator
             */
            explicit
            CartesianTopology(const int dim, const Communicator &communicator = Communicator());
           
            /**
             * \brief uses the MPI_Dims_create to genarate the processor grid
             * \param dimensions the number of dimension of the processor grid
             * \param communicator the communicator
             */
            CartesianTopology(const Array< int > &dimensions, const Communicator &communicator = Communicator());
            
            ///Copies
            CartesianTopology(const CartesianTopology &other);
           
            ///Copies
            CartesianTopology & operator = (const CartesianTopology &other);
            
            /**
             * The Destructor
             */
            ~CartesianTopology();
            
            
            inline NeighborIterator neighborsBegin()
            {
                return neighbors.begin();
            }
            
            inline NeighborIterator neighborsEnd()
            {
                return neighbors.end();
            }
            
            
            
            /**
             * \param d the dimension
             * \param disp the displacement in direction d
             * \return the rank of the processor with respect of d and disp
             */
            inline
            int shift(int d, int disp) const
            {
                int rank = communicator.getRank(), dest = 0; 
                NSE_SOLVER_CATCH_ERROR(MPI_Cart_shift(cartComm, d, disp, &rank , &dest));
                return dest;
            }
            
            
            /**
             * \param d the dimension
             * \param disp the displacement in direction d
             * \return if there is no other processor in directioon (d, disp)
             */
            inline
            bool isBoundary(int d, int disp) const
            {
                NSE_SOLVER_ASSERT( abs(disp) == 1 );
                return Communicator::isNull(shift(d, disp));
            }
            
            /// \return the communicator associated with the topology
            inline Communicator & getCommunicator() 
            {
                return communicator;
            }
            
            /// \return the communicator associated with the topology
            inline const Communicator & getCommunicator() const
            {
                return communicator;
            }
            
            /// \return the dimensions associated of the processor grid
            inline const Array< int > & getDimensions() const
            {
                return dimensions;
            }
            
             /// \return the coordinats associated of the processor 
            inline const Array< int > & getCoordinates() const
            {
                return coordinates;
            }
            
            
            /// \return the number of cartesian dimensions
            inline const int getNumberOfDims() const
            {
                return dimensions.size();
            }
            
            
        };
    }
}

#endif //PIDAMRX_CartesianTopology_H
